/*
 * $Id: PerforceSynchronizerRunnable.java 23 2002-09-29 13:59:30Z bpruessmann $
 * 
 * Copyright (c) 2001 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 */
package net.sourceforge.p4eclipse.core;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import jdbm.JDBMHashtable;
import jdbm.JDBMRecordManager;
import net.sourceforge.p4eclipse.core.api.ICountersResult;
import net.sourceforge.p4eclipse.core.api.IDescribeResult;
import net.sourceforge.p4eclipse.core.api.IWhereResult;
import net.sourceforge.p4eclipse.core.syncinfo.ResourceSyncInfo;
import net.sourceforge.p4eclipse.ui.PerforceUIPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;

/**
  * @version 	$Revision: 23 $
  * @author		<a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class PerforceSynchronizerRunnable implements Runnable
{
	private static final String LAST_CHANGELIST_KEY = "lastChangelist"; //$NON-NLS-1$
	
	private Thread ownerThread;
	private JDBMHashtable dataStore;
	private boolean shutdown = false;
	private int lastChangelist = -1;

	/**
	  * Creates a new PerforceSynchronizerRunnable object.
	  */
	public PerforceSynchronizerRunnable(JDBMRecordManager recordManager)
		throws PerforceException, IOException
	{
		this.dataStore = recordManager.getHashtable("PerforceSynchronizerRunnable"); //$NON-NLS-1$
		retrieveStoredState();
	}

	/**
	  * Stops the background update thread.
	  */ 
	public void shutdown()
	{
		shutdown = true;
		if (ownerThread != null)
		{
			ownerThread.interrupt();
			try
			{
				ownerThread.join();
			}
			catch (InterruptedException e)
			{
				e = e;
			}
		}
	}
	
	/**
	  * Resets the internal state.
	  */
	public void reset()
		throws PerforceException
	{
		lastChangelist = -1;
		wakeup();
	}
	
	/*
	 * @see Runnable#run()
	 */
	public void run()
	{
		ownerThread = Thread.currentThread();
		while (!shutdown)
		{
			if (isEnabled())
			{
				if (!PerforceUIPlugin.getClient(true).dropped() && 
				    lastChangelist == -1)
				{
					retrieveChangelistCounter();
				}
	
				if (!PerforceUIPlugin.getClient(true).dropped() &&
				    lastChangelist != -1)
				{
					int latestChangelist = lastChangelist;
					try
					{			
						ICountersResult countersResult = PerforceUIPlugin.getClient(true).counters();
						latestChangelist = countersResult.getCounterValue(ICountersResult.CHANGE).intValue();
					}
					catch (PerforceException e)
					{
						PerforceUIPlugin.getPlugin().log(e.getStatus());
					}				
					
					if (latestChangelist > lastChangelist)
					{
						IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
						IProject[] projects = PerforceUIPlugin.getPlugin().getWorkspaceRoot().getProjects();
						
						List affectedResources = new ArrayList();
						for (int changelist = lastChangelist + 1; changelist < latestChangelist + 1; changelist++)
						{
							try
							{
								IDescribeResult describeResult = PerforceUIPlugin.getClient().describe(new Integer(changelist));
								
								ResourceSyncInfo[] affectedFiles = describeResult.getAffectedFiles();
								for (int i = 0, iLength = affectedFiles.length; i < iLength; i++)
								{
									String localFilename = getLocalFilename(affectedFiles[i].getDepotFileName());
									
									IFile file = workspaceRoot.getFileForLocation(
										new Path(localFilename));
									
									if (file != null)
									{
										affectedResources.add(file);
									}
								}
							}
							catch (PerforceException e)
							{
								PerforceUIPlugin.log(e.getStatus());
							}
						}
		
						if (!affectedResources.isEmpty())
						{				
							IResource[] resources = (IResource[])affectedResources.toArray(new IResource[affectedResources.size()]);
							PerforceUIPlugin.getPlugin().broadcastResourceStateChanges(resources);
						}		
		
						lastChangelist = latestChangelist;
						try
						{
							dataStore.put(LAST_CHANGELIST_KEY, new Integer(lastChangelist));
						}
						catch (IOException e)
						{
							PerforceUIPlugin.getPlugin().logError("PerforceSynchronizerRunnable.run failed. Shutting down...", e);
							shutdown = true;
						}
					}			
				}
			}
			waitForNextCacheUpdate();			
		}
	}
	
	public static void wakeup()
	{
		synchronized (PerforceSynchronizerRunnable.class)
		{
			PerforceSynchronizerRunnable.class.notifyAll();
		}		
	}

	/**
	  * Returns the local filename of a depot file.
	  */
	private static String getLocalFilename(String depotFilename) 
		throws PerforceException 
	{
		IWhereResult whereResult = PerforceUIPlugin.getClient().where(depotFilename);
		String localFilename = whereResult.getLocalFilename();
		
		return localFilename;
	}

	/**
	  * Suspends the thread until the next schedules cache update occurs.
	  */
	private static void waitForNextCacheUpdate() 
	{
		try 
		{
			long sleep = PerforceUIPlugin.getPlugin().getPreferenceStore().getLong(PerforceUIPlugin.CACHE_UPD_INTERVAL_PREFERENCE) * 1000;
			
			synchronized (PerforceSynchronizerRunnable.class)
			{
				PerforceSynchronizerRunnable.class.wait(sleep);
			}
		} 
		catch(InterruptedException e) 
		{
		}
	}
	
	private void retrieveChangelistCounter()
	{
		try
		{
			ICountersResult countersResult = PerforceUIPlugin.getClient(true).counters();
			lastChangelist = countersResult.getCounterValue(ICountersResult.CHANGE).intValue();
	
			try
			{
				dataStore.put(LAST_CHANGELIST_KEY, new Integer(lastChangelist));
			}
			catch (IOException e)
			{
				PerforceUIPlugin.getPlugin().logError("PerforceSynchronizerRunnable.reset failed.", e);
			}
		}
		catch (PerforceException e)
		{
			PerforceUIPlugin.log(e.getStatus());
		}
	}
	
	private void retrieveStoredState()
	{
		Integer lastChangelistObject = null;
		try
		{
			lastChangelistObject = (Integer)dataStore.get(LAST_CHANGELIST_KEY);
		}
		catch (IOException e)
		{
			PerforceUIPlugin.getPlugin().logError("Failure in PerforceSynchronizerRunnable.reset.", e);
		}
		
		if (lastChangelistObject != null)
		{
			lastChangelist = lastChangelistObject.intValue();
		}		
	}	
	
	private boolean isEnabled()
	{
		return PerforceUIPlugin.getPlugin().getPreferenceStore().getBoolean(PerforceUIPlugin.CACHE_UPD_ENABLED);
	}
}
